/************************************************************************************************************************************************************
*                          DESENVOLVEDORES: Denis Tobias; Diogo Oliveira; Marcel M. Marum; Marcos F. Rachid; Rodrigo M. Botelho
*                                       ORIENTADOR e DESENVOLVEDOR: Prof.Dr. Eraldo Pereira Marinho
*
*                                                           Arquivo: lexer.c
*
************************************************************************************************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "tokens.h"
#include "keywords.h"
#include "lexer.h"

FILE * tape;//arquivo de entrada.
int line = 1;//numero de linhas da entrada.
int column = 1;

//função que pula espaços vazios e comentarios e incrementa o numero de linhas.
void
skipspaces(void)
{
    int             head;
    while(isspace(head = getc(tape))){
	if(head == '\n'){line++;column = 1;}
	column++;
    }
    column--;
    ungetc(head, tape);
    if((head = getc(tape)) == '{'){
	column++;
//	printf("entrou coment!\n");
	while ((head = getc(tape)) != '}'){
		column++;
		if(head == '\n'){line++;column = 1;}
	}
	column++;
	skipspaces();
    }else{
	ungetc(head, tape);
	column--;
    }
}

//função que identifica a palavra como ID e verifica se a palavra é uma palavra chave.
/*
 *ID -> alpha (alphaint)*
 */
isID(void)
{
    int             i = 0;
    lexeme[i] = toupper(getc(tape));
    column++;
//    printf("%s\n", lexeme);

    if (isalpha(lexeme[i])) {
        while (isalnum(lexeme[++i] = toupper(getc(tape))))column++;
	column--;
        ungetc(lexeme[i], tape);
        lexeme[i] = 0;
        if (i = iskeyword(lexeme))
//	printf("entrou\n");
            return i;
        return ID;
    }
    column--;
    ungetc(lexeme[i], tape);
    return 0;
}

char            lexeme[MAXLEN];//palavra que será usada nas funções

int             i;//inteiro auxiliar para usar na string.

//função que identifica a palavra como UINT(inteiro)
/*
 *UINT -> (1-9)digit | '0'
 */
isUINT(void)
{
    i = 0;
    lexeme[i] = getc(tape);
    column++;
    if (isdigit(lexeme[i])) {
        if (lexeme[i] == '0') {
            lexeme[1] = 0;
            return UINT;
        }
        while (isdigit(lexeme[++i] = getc(tape)))column++;
	column--;
        ungetc(lexeme[i], tape);
        lexeme[i] = 0;
        return UINT;
    }
    column--;
    ungetc(lexeme[i], tape);
    return 0;
}

//função que identifica numeros real.
/*
 * floating = UINT ('.' digit* enotation? | enotation) | '.' digit+ enotation?
 *
 * enotation = 'E' ('+'|'-')? digit+
 */
isenotation(void)
{
    int lenght = 1;
    lexeme[i] = getc(tape);
    column++;
    if (lexeme[i] == 'E' || lexeme[i] == 'e') {
        lexeme[++i] = getc(tape);
	column++;
        if (lexeme[i] == '+' || lexeme[i] == '-'){
            lexeme[++i] = getc(tape);
	    column++;
	}
        if (isdigit(lexeme[i])) {
            while (isdigit(lexeme[++i] = getc(tape))){
	    	lenght++;
		column++;
	    }
	    column--;
            ungetc(lexeme[i], tape);
            lexeme[i] = 0;
            return lenght;
        }
        fprintf(stderr, "ilegal exponential notation\n");
        exit(-2);
    }
    column--;
    ungetc(lexeme[i], tape);
    lexeme[i] = 0;
    return 0;
}

isNUM(void)
{
    int lenght;
    if (isUINT()) {
        if ((lexeme[i] = getc(tape)) == '.') {
	    column++;
            while (isdigit(lexeme[++i] = getc(tape)))column++;
	    column--;
            ungetc(lexeme[i], tape);
            lexeme[i] = 0;
            isenotation();
            return FLOAT;
        }
	column--;
        ungetc(lexeme[i], tape);
        lexeme[i] = 0;
	lenght = isenotation();
        if (lenght>0)
            return FLOAT;
        return UINT;
    }
    if ((lexeme[i] = getc(tape)) == '.') {
        if (isdigit(lexeme[++i] = getc(tape))) {
	    column++;
            while (isdigit(lexeme[++i] = getc(tape)))column++;
	    column--;
            ungetc(lexeme[i], tape);
            lexeme[i] = 0;
            isenotation();
            return FLOAT;
        }
	column--;
        ungetc(lexeme[i--], tape);
	column--;
        ungetc('.', tape);
        return 0;
    }
    column--;
    ungetc(lexeme[i], tape);
    return 0;
}
//função que identifica strings e char.
/*
 * CTE -> ' (caracter)* '
 *
 * CHAR -> ' caracter '
 */
isCTE(void)
{
   int i = 0, lenght = 0;
   if((lexeme[i++] = getc(tape)) == '\''){
      column++;
      while((lexeme[i++] = getc(tape)) != '\''){
	lenght++;
	column++;
      }
      lexeme[i] = 0;
      if(lenght == 1 || lenght == 0){
        return CHR;
      }else{
	return CTE;
      }
   }
   column--;
   ungetc(lexeme[--i], tape);
   return 0;
}

//função que identifica o operador relacional >=
/*
 * GEQ -> > =
 */
isGEQ(void)
{
  int i = 0;
  if((lexeme[i++] = getc(tape)) == '>'){
     column++;
     if((lexeme[i++] = getc(tape)) == '='){
	column++;
        lexeme[i] = 0;
        return GEQ;
     }
     column--;
     ungetc(lexeme[--i], tape);
     lexeme[i] = 0;
     return lexeme[0];
  }
  column--;
  ungetc(lexeme[--i], tape);
  return 0;

}

//função que identifica os operadores relacionais <= e <>
/*
 * LEQ -> < =
 *
 * NEQ -> < >
 */
isLEQ(void)
{
  int i = 0;
  if((lexeme[i++] = getc(tape)) == '<'){
     column++; 
     if((lexeme[i++] = getc(tape)) == '='){
	column++;
        lexeme[i] = 0;
        return LEQ;
     }else if(lexeme[i-1] == '>'){
	lexeme[i] = 0;
	return NEQ;
     }
     column--;
     ungetc(lexeme[--i], tape);
     lexeme[i] = 0;
     return lexeme[0];
  }
  column--;
  ungetc(lexeme[--i], tape);
  return 0;
}

//função que identifica o operador de assignment.
/*
 * ASGNM -> : =
 */
isASGNM(void)
{
	int i = 0;
	if((lexeme[i++]=getc(tape))==':'){
		column++;
		if((lexeme[i++]=getc(tape))=='='){
			column++;
			lexeme[i] = 0;
			return ASGNM;
		}
		column--;
		ungetc(lexeme[--i],tape);
		lexeme[i]=0;
		return lexeme[0];
	}
	column--;
	ungetc(lexeme[--i],tape);
	return 0;
}

//função usada para identificar o lexeme como ID ou UINT ou FLOAT ou STRING ou CHAR ou um dos operadores.
gettoken(void)
{
    int             token;
    skipspaces();
    if (token = isID())
        return token;
    if (token = isNUM())
        return token;
    if (token = isCTE())
        return token;
    if (token = isGEQ())
	return token;
    if (token = isLEQ())
	return token;
    if (token = isASGNM())
	return token;
    lexeme[1] = 0;
    return lexeme[0] = getc(tape);
    column++;
}

int             lookahead;//variavel que identifica a palavra esperada no parser.

//função que identifica erro ou acerto nas regras sintáticas.
void
match(int expected)
{
//    printf("%d %d\n",expected,lookahead);
    if (expected == lookahead) {
        lookahead = gettoken();
    } else {
	printf("expected:'%c' received:'%c'\n",expected,lookahead);
        fprintf(stderr, "Line: %d,%d - token mismatch\naborting\n", line,column);
	exit(-1);
    }
}
